#include <MAUtil/Moblet.h>
#include <MAUI/Screen.h>
#include <MAUI/Label.h>
#include <MAUI/ListBox.h>
#include <MAUtil/Vector.h>
#include <MAUtil/Set.h>
#include <MAUtil/List.h>
#include <matime.h>
#include <mawvsprintf.h>
#include "Cell.h"
#include "MAheaders.h"
#include <mavsprintf.h>
#include "mastdlib.h"

using namespace MAUtil;
using namespace MAUI;

#define X_OFFSET 0
#define Y_OFFSET 0
#define X_SIZE 12
#define Y_SIZE 14
#define CELL_SIZE 20

/**
 * MAUI is short for MoSync API User Interface.
 * A Screen is a MAUI object that holds widgets.
 */
class MAUIScreen : public Screen
{
public:

	MAUIScreen()
	{
		srand(maTime());
		m_backgroundArea = new Label(0, 0, 0, 0, NULL);
		m_backgroundArea->setBackgroundColor(0xDDDDDDDD);

		m_minesButton = new Image(  10, 280, 60, 40, m_backgroundArea, false, false, MINETOGGLE);
		m_resetButton = new Image(  90, 280, 60, 40, m_backgroundArea, false, false, RESET);
		m_quitButton =  new Image( 170, 280, 60, 40, m_backgroundArea, false, false, QUIT);

		m_mineDisplay = false;
		for(int i=0; i<X_SIZE; i++){
			for(int j=0; j<Y_SIZE; j++){
				m_cells[i][j] == NULL;
			}
		}

		PopulateGrid();
		m_score = 0;

		// Set the main widget of the screen. This will
		// resize the widget to fit the screen.
		setMain(m_backgroundArea);
	}

	//are the x,y coordinates over a cell
	bool ValidCell(const int& p_x, const int& p_y){
		return !(p_x < 0 || p_x >= X_SIZE || p_y < 0 || p_y >= Y_SIZE);
	}


	//recreate the level
	void PopulateGrid(){
		m_fail = false;
		m_mineDisplay = false;
		//create the cells
		for(int i=0; i<X_SIZE; i++){
			for(int j=0; j<Y_SIZE; j++){
				if(m_cells[i][j] != NULL){
					delete m_cells[i][j];
					m_cells[i][j] == NULL;
				}

				m_cells[i][j] = new Cell(CELL_SIZE * i + X_OFFSET, CELL_SIZE * j + Y_OFFSET , m_backgroundArea);
			}
		}

		//place mines
		int random = rand() % X_SIZE*Y_SIZE/8;
		int mineCount = X_SIZE * Y_SIZE / 16 + random;
		for(int i=0; i<mineCount;i++){
			int x,y;
			do{
				x=rand() % X_SIZE;
				y=rand() % Y_SIZE;
			}while(m_cells[x][y]->IsMine());
			m_cells[x][y]->SetMine(true);
			for(int xVal=-1; xVal<2; xVal++){
				for(int yVal=-1; yVal<2; yVal++){
					if(!ValidCell(x+xVal, y+yVal) || (xVal ==0 && yVal == 0))
						continue;
					m_cells[x+xVal][y+yVal]->IncrementNearbyMineCount();
				}
			}
		}
	}

	//should all mines be revealed
	void RevealAllMines(const bool& p_revealed){
		for(int i=0; i<X_SIZE; i++){
			for(int j=0; j<Y_SIZE; j++){
				if(m_cells[i][j]->IsMine()){
					if(p_revealed){
						m_cells[i][j]->SetImage(FLAG);
					}else{
						m_cells[i][j]->SetImage(CELL);
					}
				}
			}
		}
	}

	//clear out an area when a cell near no mines is clicked
	void ClearEmptyArea(const MAPoint2d& p_point){

		MAUtil::Vector<Cell*> cellList;
		cellList.add(m_cells[p_point.x][p_point.y]);
		MAPoint2d currentPoint;
		do{
			currentPoint = ConvertScreenCoordToCellCoord(cellList[0]->GetLocation());
			cellList.remove(0);
			for(int x = -1; x<2; x++){
				for(int y = -1; y<2; y++){

					if(!ValidCell(currentPoint.x+x, currentPoint.y+y)){
						continue;
					}

					Cell* cell = m_cells[currentPoint.x+x][currentPoint.y+y];
					if(cell->IsMine()){
						continue;
					}

					if(cell->NearbyMineCount() == 0 && !cell->IsExposed()){
						cellList.add(cell);
					}

					cell->ExposeCell();
				}
			}
		}while(cellList.size() > 0);
	}

	/**
	 * Deallocate objects in the destructor.
	 */
	virtual ~MAUIScreen()
	{
		delete m_backgroundArea;
	}

	/**
	 * Called when a key is pressed.
	 */
	virtual void keyPressEvent(int keyCode, int nativeCode)
	{ }

	/**
	 * Called when the screen is touched.
	 */
	virtual void pointerPressEvent(MAPoint2d point)
	{
		MAPoint2d cellCoord = ConvertScreenCoordToCellCoord(point);

		// button click check
		if(!ValidCell(cellCoord.x, cellCoord.y)){
			CheckButtons(point);
			return;
		}

		//if mine has previously been hit
		if(m_fail){
			return;
		}

		maSoundStop();
		//try to click a mine, if true is returned then a mine was hit
		if(m_cells[cellCoord.x][cellCoord.y]->OnClick()){
			maSoundPlay(BAD, 0, maGetDataSize(BAD));
			RevealAllMines(true);
			m_cells[cellCoord.x][cellCoord.y]->SetImage(MINE);
			m_fail = true;
		}else if(m_cells[cellCoord.x][cellCoord.y]->NearbyMineCount() == 0){
			//area cleared
			m_score++;
			ClearEmptyArea(cellCoord);
			maSoundPlay(GOODTWO, 0, maGetDataSize(GOODTWO));
		}else{ //single cell cleared
			m_score++;
			maSoundPlay(GOODONE, 0, maGetDataSize(GOODONE));
		}

		// We need to repaint the parent when a child
		// widget is changed.
		m_backgroundArea->requestRepaint();
	}

private:

	Label* m_backgroundArea;
	Image* m_resetButton;
	Image* m_minesButton;
	Image* m_quitButton;
	Cell* m_cells[X_SIZE][Y_SIZE];
	bool m_fail;
	int m_screenPressTime;
	bool m_mineDisplay;
	int m_score;

	//check if any buttons were pressed
	void CheckButtons(MAPoint2d point){
		if(point.y < 240)
			return;

		if(point.x > 170){
			Moblet::close();

		}else if(point.x > 90){
			PopulateGrid();
		}else{
			if(m_fail)
				return;
			m_mineDisplay = !m_mineDisplay;
			RevealAllMines(m_mineDisplay);
		}

		m_backgroundArea->requestRepaint();
	}

	//get the cell at specific screen coordinates
	MAPoint2d ConvertScreenCoordToCellCoord(const MAPoint2d& p_point){
		MAPoint2d cellCoord;
		cellCoord.x = (p_point.x-X_OFFSET)/CELL_SIZE;
		cellCoord.y = (p_point.y-Y_OFFSET)/CELL_SIZE;
		return cellCoord;
	}
};

/**
 * A Moblet is a high-level class that defines the
 * behaviour of a MoSync program.
 *
 * To use MAUI you need a Moblet, but a  Moblet can
 * be used with or without the MAUI library.
 */
class MAUIMoblet : public Moblet
{
public:
	/**
	 * Initialize the application in the constructor.
	 */
	MAUIMoblet()
	{
		mScreen = new MAUIScreen();
		mScreen->show();
	}

	/**
	 * Deallocate objects in the destructor.
	 */
	virtual ~MAUIMoblet()
	{
		delete mScreen;
	}

	/**
	 * Called when a key is pressed.
	 */
	void keyPressEvent(int keyCode, int nativeCode)
	{
	}

private:
	MAUIScreen* mScreen;
};

/**
 * Entry point of the program. The MAMain function
 * needs to be declared as extern "C".
 */
//extern "C" int MAMain()
//{
//	Moblet::run(new MAUIMoblet());
//	return 0;
//}
